﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net.Sockets;
using System.Net;
using System.Threading;
using Jayden.Dll.Cards.Poker.Enums;
using System.IO;
using System.Drawing;
using System.Drawing.Imaging;
using System.Security.Cryptography.X509Certificates;
using System.Security.Authentication;
using System.Timers;

namespace Jayden.Dll.Cards.Poker
{
	public abstract class PokerServer
	{
		private int m_Port;
		private List<PokerClientThread> m_Clients = new List<PokerClientThread>();
		private HashSet<string> m_Players = new HashSet<string>();
		private Dictionary<string, PokerTable> m_CashTables = new Dictionary<string, PokerTable>();
		private Dictionary<string, PokerTournamentConfiguration> m_Tournaments = new Dictionary<string, PokerTournamentConfiguration>();

		private X509Certificate m_Certificate;
		private bool m_UseSsl = true;

		private Socket m_Socket;

		public PokerServer(int port)
		{
			m_Port = port;
			m_Socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

			CreateCashTable("MicoStakeEUR", new PokerCashGameConfiguration(9, PokerLimit.NoLimit, PokerType.TexasHoldem, PokerCurrency.EUR, 0, 1, 2, 100, 400));
			CreateCashTable("PlayMoney", new PokerCashGameConfiguration(9, PokerLimit.NoLimit, PokerType.TexasHoldem, PokerCurrency.Play, 0, 10, 25, 1000, 5000));
			CreateTournament("MicroStakeTournament", /*new DateTime(2013, 8, 1, 12, 10, 0)*/DateTime.Now, new PokerTournamentConfiguration("MicroStakeTournament", 2, 18, NewLevels(
				5 * 60 * 1000,
				5, 10, 0,
				10, 20, 0,
				15, 30, 0,
				20, 40, 0,
				20, 40, 5,
				25, 50, 5,
				35, 70, 10,
				45, 90, 10,
				55, 110, 10,
				80, 160, 20,
				105, 210, 25,
				130, 260, 25,
				150, 300, 25,
				200, 400, 50,
				300, 600, 75,
				400, 800, 100
			)));
		}

		private PokerTournamentConfiguration.BettingStructureEntry[] NewLevels(int duration, params ulong[] amounts)
		{
			List<PokerTournamentConfiguration.BettingStructureEntry> entries = new List<PokerTournamentConfiguration.BettingStructureEntry>();
			int count = amounts.Length / 3;
			for (int c = 0; c < count; c++)
				entries.Add(NewLevel(c, duration, amounts[c * 3], amounts[c * 3 + 1], amounts[c * 3 + 2]));
			return entries.ToArray();
		}
		private PokerTournamentConfiguration.BettingStructureEntry NewLevel(int level, int duration, ulong smallBlind, ulong bigBlind, ulong ante = 0)
		{
			PokerTournamentConfiguration.BettingStructureEntry entry = new PokerTournamentConfiguration.BettingStructureEntry();
			entry.Level = level;
			entry.Duration = duration;
			entry.Ante = ante;
			entry.SmallBlind = smallBlind;
			entry.BigBlind = bigBlind;
			return entry;
		}

		public X509Certificate Certificate { get { return m_Certificate; } }

		public List<PokerTable> CashGameTables
		{
			get
			{
				lock (m_CashTables)
				{
					return new List<PokerTable>(m_CashTables.Values);
				}
			}
		}

		public bool IsAlreadyConnected(string login)
		{
			lock (m_Players)
			{
				return m_Players.Contains(login);
			}
		}
		public void Connected(string login)
		{
			lock (m_Players)
			{
				m_Players.Add(login);
			}
		}

		public void Run()
		{
			string ipAddress = "127.0.0.1";
			if (File.Exists("server.txt"))
				ipAddress = File.ReadAllLines("server.txt")[0];
			else
				File.WriteAllLines("server.txt", new string[] { ipAddress });
			if(m_UseSsl)
				m_Certificate = new X509Certificate2("certificate.pfx");
			IPAddress ip = IPAddress.Parse(ipAddress);
			OnServerMessage(string.Format("Starting server on {0}:{1}.", ip, m_Port));
			m_Socket.Bind(new IPEndPoint(ip, m_Port));
			m_Socket.Listen(100);
			OnServerMessage(string.Format("Waiting for clients ..."));
			while (true)
			{
				Socket socket = m_Socket.Accept();
				PokerSocket pokerSocket = new PokerSocket(socket);
				PokerClientThread thread = new PokerClientThread(this, pokerSocket);
				lock (m_Clients)
				{
					m_Clients.Add(thread);
					OnServerMessage("New client connected ...");
				}
				thread.Start();
			}
		}

		public void DropConnection(PokerClientThread thread)
		{
			lock (m_Clients)
			{
				OnServerMessage(string.Format("Removing client {0}.", thread));
				if (thread.Player != null)
				{
					lock (m_Players)
					{
						m_Players.Remove(thread.Player.Login);
					}
				}
				m_Clients.Remove(thread);
			}
		}
		public void CreateCashTable(string name, PokerCashGameConfiguration configuration)
		{
			PokerTable table = new PokerTable(name, configuration);
			table.Dealer = new TexasHoldemDealer();
			lock (m_CashTables)
			{
				m_CashTables.Add(table.TableName, table);
			}
			table.Dealer.Start();
		}
		public void CreateTournament(string name, DateTime startTime, PokerTournamentConfiguration configuration)
		{
			lock (m_Tournaments)
			{
				m_Tournaments.Add(name, configuration);
			}
			if (File.Exists("tournament.txt"))
			{
				string[] players = File.ReadAllLines("tournament.txt");
				foreach(string player in players)
					if(!string.IsNullOrWhiteSpace(player))
						configuration.Enter(LoadPlayer(player, "password", null));
			}
			configuration.Start(startTime);
		}

		public PokerTable GetTable(string tableName, bool cashGameOnly = false)
		{
			PokerTable table = null;
			lock (m_CashTables)
			{
				if (m_CashTables.TryGetValue(tableName, out table))
					return table;
			}
			if (cashGameOnly)
				return null;
			lock (m_Tournaments)
			{
				foreach (PokerTournamentConfiguration config in m_Tournaments.Values)
					foreach (PokerTable tournamentTable in config.Tables)
						if (tournamentTable.TableName == tableName)
							return tournamentTable;
			}
			
			return null;
		}

		public abstract void OnServerMessage(string message);
		public abstract void OnClientMessage(PokerClientThread client, string message);

		public virtual PokerPlayer LoadPlayer(string login, string password, PokerSocket socket)
		{
			return PokerPlayer.GetPlayer(login, password, 1000, socket);
		}
		public virtual void SavePlayer(PokerPlayer player) { }

		public List<PokerTournamentConfiguration> Tournaments
		{
			get
			{
				lock (m_Tournaments)
				{
					return new List<PokerTournamentConfiguration>(m_Tournaments.Values);
				}
			}
		}

		public PokerTournamentConfiguration GetTournament(string tournamentName)
		{
			lock (m_Tournaments)
			{
				PokerTournamentConfiguration result = null;
				if (!m_Tournaments.TryGetValue(tournamentName, out result))
					return null;
				return result;
			}
		}
	}

	public class PokerClientThread
	{
		private Thread m_Thread;

		private PokerServer m_Server;
		private PokerSocket m_Socket;
		private System.Timers.Timer m_KeepAliveTimer;
		private bool m_Running = false;

		private List<PokerTable> m_Tables = new List<PokerTable>();
		private PokerPlayer m_Player;

		public PokerClientThread(PokerServer server, PokerSocket socket)
		{
			m_Server = server;
			m_Socket = socket;
		}

		public void Start()
		{
			if (m_Thread != null)
				throw new Exception("thread already started.");
			m_Thread = new Thread(Run);
			m_Thread.Start();
		}

		public PokerPlayer Player { get { return m_Player; } }

		private void KeepAlive(object source, ElapsedEventArgs e)
		{
			if (m_Running)
			{
				try
				{
					m_Socket.KeepAlive();
				}
				catch (Exception exc)
				{
					m_Server.OnClientMessage(this, "Exception catched while keeping alive. Stopping ...");
					m_Running = false;
				}
			}
		}

		protected void Run()
		{
			try
			{
				m_Server.OnClientMessage(this, "Waiting for handshake ...");
				m_Running = true;
				ulong header = m_Socket.ReadUInt64();
				if (header != PokerProtocol.Header)
					throw new Exception("invalid header.");
				string protocolName = m_Socket.ReadString();
				if (protocolName != PokerProtocol.ProtocolName)
					throw new Exception("invalid protocol name.");
				ulong version = m_Socket.ReadUInt64();
				if (version != PokerProtocol.Version)
					throw new Exception("invalid protocol version.");

				m_Server.OnClientMessage(this, "Handshake accepted ...");
				if (m_Server.Certificate != null)
				{
					m_Socket.SslStream.AuthenticateAsServer(m_Server.Certificate);
					m_Socket.Encrypted = true;
					m_Server.OnClientMessage(this, "Connection secured with ssl ...");
				}

				m_Socket.SendMessage("Welcome to PokerOnline Server v1.0.");
				m_KeepAliveTimer = new System.Timers.Timer();
				m_KeepAliveTimer.Elapsed += new ElapsedEventHandler(KeepAlive);
				m_KeepAliveTimer.Interval = 30000;
				m_KeepAliveTimer.Enabled = true;

				int messageId = 0x00;
				while (m_Running)
				{
					messageId = m_Socket.ReadInt32();
					//m_Server.OnClientMessage(this, "Received message #" + messageId + ". Executing ...");
					OnReceivingMessage(messageId);
				}
			}
			catch(Exception e)
			{
				m_Server.OnClientMessage(this, "Exception catched. Stopping ...");
				m_Running = false;
			}
			m_KeepAliveTimer.Enabled = false;
			m_KeepAliveTimer = null;

			foreach (PokerTable table in m_Tables)
			{
				PokerTableSeat seat = table[m_Socket];
				if (seat != null)
				{
					seat.Player.SetAmount(table, 0, 0);
					table.Leave(seat.Index);
				}
				table.RemoveObserver(m_Socket);
			}
			m_Tables.Clear();
			if(m_Player != null)
				m_Server.SavePlayer(m_Player);
			m_Server.DropConnection(this);
		}

		protected void OnReceivingMessage(int messageId)
		{
			switch ((PokerProtocolMessageType)messageId)
			{
				case PokerProtocolMessageType.KeepAlive:
					// client is still connected and replying
					break;
				case PokerProtocolMessageType.Disconnect:
					OnDisconnect();
					break;
				case PokerProtocolMessageType.Message:
					m_Server.OnClientMessage(this, m_Socket.ReadString());
					break;
				case PokerProtocolMessageType.Login:
					OnLogin();
					break;
				case PokerProtocolMessageType.Logout:
					OnLogout();
					break;
				case PokerProtocolMessageType.Observe:
					OnObserve();
					break;
				case PokerProtocolMessageType.Leave:
					OnLeave();
					break;
				case PokerProtocolMessageType.AskBuyIn:
					OnAskBuyIn();
					break;
				case PokerProtocolMessageType.Sit:
					OnSit();
					break;
				case PokerProtocolMessageType.ReBuy:
					OnReBuy();
					break;
				case PokerProtocolMessageType.Action:
					OnAction();
					break;
				case PokerProtocolMessageType.ActionShow:
					OnActionShow();
					break;
				case PokerProtocolMessageType.ActionPost:
					OnActionPost();
					break;
				case PokerProtocolMessageType.ChatMessage:
					OnChatMessage();
					break;
				case PokerProtocolMessageType.SitOut:
					OnSitOut();
					break;
				case PokerProtocolMessageType.Waiting:
					OnWaiting();
					break;
				case PokerProtocolMessageType.Lobby:
					OnLobby();
					break;
				case PokerProtocolMessageType.PlayerShowCards:
					OnPlayerShowCards();
					break;
				case PokerProtocolMessageType.TableImage:
					OnTableImage();
					break;
				case PokerProtocolMessageType.RequestTableImage:
					OnRequestTableImage();
					break;
				case PokerProtocolMessageType.Bankroll:
					OnBankroll();
					break;
				case PokerProtocolMessageType.TournamentLobby:
					OnTournamentLobby();
					break;
				case PokerProtocolMessageType.TournamentRegister:
					OnTournamentRegister();
					break;
				case PokerProtocolMessageType.TournamentUnregister:
					OnTournamentUnregister();
					break;
				case PokerProtocolMessageType.TournamentLobbyPlayers:
					OnTournamentLobbyPlayers();
					break;
				case PokerProtocolMessageType.TournamentLobbyBettingStructure:
					OnTournamentLobbyBettingStructure();
					break;
				case PokerProtocolMessageType.TournamentLobbyPayoutStructure:
					OnTournamentLobbyPayoutStructure();
					break;
				case PokerProtocolMessageType.TournamentRebuy:
					OnTournamentRebuy();
					break;
				default:
					throw new Exception("unknow message #" + messageId + ".");
			}
		}

		protected void OnDisconnect()
		{
			m_Server.OnClientMessage(this, string.Format("Disconnecting ..."));
			m_Running = false;
			if (m_Player != null)
				m_Player.Socket = null;
		}
		protected void OnLogin()
		{
			string login = m_Socket.ReadString();
			string password = m_Socket.ReadString();

			if (m_Player != null)
				return;

			lock (m_Socket)
			{
				m_Socket.WriteInt32((int)PokerProtocolMessageType.Login);
				if (m_Server.IsAlreadyConnected(login))
				{
					m_Socket.WriteBoolean(false);
					m_Socket.WriteString(login);
					m_Socket.WriteString("already connected");
				}
				else
				{
					m_Player = m_Server.LoadPlayer(login, password, m_Socket);
					List<PokerTableSeat> seats = m_Player.Seats;
					if (seats.Count > 0)
					{
						foreach (PokerTableSeat seat in seats)
						{
							seat.Table.AddObserver(m_Socket);
						}
					}

					if (m_Player == null)
					{
						m_Socket.WriteBoolean(false);
						m_Socket.WriteString(login);
						m_Socket.WriteString("invalid login/password");
					}
					else
					{
						m_Server.OnClientMessage(this, string.Format("Connected as '{0}'.", login));
						m_Socket.WriteBoolean(true);
						m_Socket.WriteString(login);
						m_Socket.WriteUInt64(m_Player.BankRoll);

						m_Server.Connected(login);
						m_Socket.SendMessage(string.Format("Welcome, {0}.", login));
					}
				}
			}
		}
		protected void OnLogout()
		{
			m_Server.OnClientMessage(this, string.Format("Log out ..."));
			if (m_Player != null)
				m_Player.Socket = null;
			m_Player = null;
		}
		protected void OnLobby()
		{
			PokerLimit limit = (PokerLimit) m_Socket.ReadByte();
			PokerType type = (PokerType)m_Socket.ReadByte();
			ulong minStake = m_Socket.ReadUInt64();
			ulong maxStake = m_Socket.ReadUInt64();
			ulong minBuyIn = m_Socket.ReadUInt64();
			ulong maxBuyIn = m_Socket.ReadUInt64();
			HashSet<string> currencies = new HashSet<string>(m_Socket.ReadString().Split('|'));
			byte minPlayer = m_Socket.ReadByte();
			byte maxPlayer = m_Socket.ReadByte();
			byte minSeat = m_Socket.ReadByte();
			byte maxSeat = m_Socket.ReadByte();

			List<PokerTable> tables = m_Server.CashGameTables;
			List<PokerTable> results = new List<PokerTable>();
			foreach (PokerTable table in tables)
			{
				if (table.Limit != limit)
					continue;
				if (table.Type != type)
					continue;
				if (table.BigBlind < minStake)
					continue;
				if (table.BigBlind > maxStake)
					continue;
				if (!currencies.Contains(table.Currency.IsoCode))
					continue;
				if (table.NumberOfSeats < minSeat)
					continue;
				if (table.NumberOfSeats > maxSeat)
					continue;
				results.Add(table);
			}
			lock (m_Socket)
			{
				m_Socket.WriteInt32((int)PokerProtocolMessageType.Lobby);
				m_Socket.WriteInt32(results.Count);
				for (int c = 0; c < results.Count; c++)
				{
					PokerTable table = results[c];
					PokerCashGameConfiguration cashGame = table.Configuration as PokerCashGameConfiguration;
					m_Socket.WriteString(table.TableName);
					m_Socket.WriteByte((byte)table.Limit);
					m_Socket.WriteByte((byte)table.Type);
					m_Socket.WriteString(table.Currency.IsoCode);
					m_Socket.WriteUInt64(table.Ante);
					m_Socket.WriteUInt64(table.SmallBlind);
					m_Socket.WriteUInt64(table.BigBlind);
					m_Socket.WriteByte((byte)table.NumberOfPlayers);
					m_Socket.WriteByte((byte)table.NumberOfSeats);
					m_Socket.WriteUInt64(cashGame.MinimumBuyIn);
					m_Socket.WriteUInt64(cashGame.MaximumBuyIn);
				}

				List<PokerTournamentConfiguration> tournaments = m_Server.Tournaments;
				m_Socket.WriteInt32(tournaments.Count);
				foreach (PokerTournamentConfiguration tournament in tournaments)
				{
					m_Socket.WriteString(tournament.TournamentName);
					m_Socket.WriteByte((byte)tournament.Limit);
					m_Socket.WriteByte((byte)tournament.Type);
					m_Socket.WriteString(tournament.EntryCurrency.IsoCode);
					m_Socket.WriteUInt64(tournament.EntryPrice);
					m_Socket.WriteUInt64(tournament.EntryRake);
					m_Socket.WriteInt32(tournament.MinimumEntries);
					m_Socket.WriteInt32(tournament.MaximumEntries);
					m_Socket.WriteDateTime(tournament.StartTime);
					m_Socket.WriteInt32(tournament.EntryCount);
				}
			}
		}
		protected void OnObserve()
		{
			string tableName = m_Socket.ReadString();
			PokerTable table = m_Server.GetTable(tableName);
			if (table != null && !m_Tables.Contains(table))
			{
				m_Server.OnClientMessage(this, string.Format("Observing table '{0}' ...", tableName));
				table.AddObserver(m_Socket);
				m_Tables.Add(table);
				table.InitTable(m_Socket);
			}
		}
		protected void OnLeave()
		{
			string tableName = m_Socket.ReadString();
			PokerTable table = m_Server.GetTable(tableName);
			if (table != null && m_Tables.Contains(table))
			{
				m_Server.OnClientMessage(this, string.Format("Leaving table '{0}' ...", tableName));
				table.RemoveObserver(m_Socket);
				m_Tables.Remove(table);
				if (m_Player != null)
				{
					PokerTableSeat seat = table[m_Player.Login];
					if (seat != null)
						table.Leave(seat.Index);
				}
			}
		}
		protected void OnAskBuyIn()
		{
			string tableName = m_Socket.ReadString();
			int seatIndex = m_Socket.ReadByte();
			PokerTable table = m_Server.GetTable(tableName, true);
			if (table != null)
			{
				PokerTableSeat seat = table[seatIndex];
				PokerCashGameConfiguration cashGame = table.Configuration as PokerCashGameConfiguration;
				if (seat != null && seat.State == PokerSeatState.Empty && cashGame != null && (cashGame.MinimumBuyIn <= m_Player.BankRoll || cashGame.Currency == PokerCurrency.Play))
				{
					lock (m_Socket)
					{
						m_Socket.WriteInt32((int)PokerProtocolMessageType.AskBuyIn);
						m_Socket.WriteString(tableName);
						m_Socket.WriteByte((byte)seatIndex);
						m_Socket.WriteUInt64(m_Player.BankRoll);
						m_Socket.WriteUInt64(cashGame.MinimumBuyIn);
						m_Socket.WriteUInt64(cashGame.MaximumBuyIn);
					}
				}
			}
		}
		protected void OnSit()
		{
			string tableName = m_Socket.ReadString();
			int seatIndex = m_Socket.ReadByte();
			ulong amount = m_Socket.ReadUInt64();
			PokerTable table = m_Server.GetTable(tableName, true);
			if (table != null && (amount < m_Player.BankRoll || table.Currency == PokerCurrency.Play))
			{
				if (table.Currency == m_Player.Currency)
					m_Player.BankRoll -= amount;
				table.Sit(seatIndex, m_Player, amount);
				m_Server.OnClientMessage(this, string.Format("Seating at table '{0}' on seat {1} with {2} ...", tableName, seatIndex, table.Currency.ToString(amount, false) + " " + table.Currency.IsoCode));
			}
		}
		protected void OnReBuy()
		{
			string tableName = m_Socket.ReadString();
			ulong amount = m_Socket.ReadUInt64();
			PokerTable table = m_Server.GetTable(tableName, true);
			if (table != null && (amount < m_Player.BankRoll || table.Currency == PokerCurrency.Play))
			{
				PokerTableSeat seat = table[m_Socket];
				if (seat != null)
				{
					if (table.Currency == m_Player.Currency)
						m_Player.BankRoll -= amount;
					table.ReBuy(seat.Index, amount);
					m_Server.OnClientMessage(this, string.Format("ReBuy at table '{0}' for {1} ...", tableName, table.Currency.ToString(amount, false) + " " + table.Currency.IsoCode));
				}
			}
		}
		protected void OnSitOut()
		{
			string tableName = m_Socket.ReadString();
			PokerTable table = m_Server.GetTable(tableName);
			if (table != null)
			{
				PokerTableSeat seat = table[m_Socket];
				if (seat != null)
				{
					seat.State = PokerSeatState.SittingOut;
					m_Server.OnClientMessage(this, string.Format("Sit out from table '{0}' as soon as possible ...", tableName));
				}
			}
		}
		protected void OnWaiting()
		{
			string tableName = m_Socket.ReadString();
			PokerTable table = m_Server.GetTable(tableName);
			if (table != null)
			{
				PokerTableSeat seat = table[m_Socket];
				if (seat != null)
				{
					seat.State = PokerSeatState.Waiting;
					m_Server.OnClientMessage(this, string.Format("Waiting to play at table '{0}' as soon as possible ...", tableName));
				}
			}
		}
		protected void OnAction()
		{
			string tableName = m_Socket.ReadString();
			int key = m_Socket.ReadInt32();
			ulong amount = m_Socket.ReadUInt64();
			PokerTable table = m_Server.GetTable(tableName);
			if (table != null)
				table[m_Player.Login].Player.SetAmount(table, amount, key);
		}
		protected void OnActionShow()
		{
			string tableName = m_Socket.ReadString();
			int key = m_Socket.ReadInt32();
			bool show = m_Socket.ReadBoolean();
			PokerTable table = m_Server.GetTable(tableName);
			if (table != null)
				table[m_Player.Login].Player.SetShowHide(table, show, key);
		}
		protected void OnActionPost()
		{
			string tableName = m_Socket.ReadString();
			int key = m_Socket.ReadInt32();
			bool post = m_Socket.ReadBoolean();
			PokerTable table = m_Server.GetTable(tableName);
			if (table != null)
				table[m_Player.Login].Player.SetPost(table, post, key);
		}

		protected void OnChatMessage()
		{
			string tableName = m_Socket.ReadString();
			string message = m_Socket.ReadString();
			PokerTable table = m_Server.GetTable(tableName);
			if (table != null)
				table.BroadCastChatMessage(m_Player.Login, message);
		}

		protected void OnPlayerShowCards()
		{
			string tableName = m_Socket.ReadString();
			CardList cards = m_Socket.ReadCards();
			PokerTable table = m_Server.GetTable(tableName);
			if (table != null)
				table.Dealer.PlayerShowCards(m_Player.Login, cards);
		}

		protected void OnTableImage()
		{
			int size = m_Socket.ReadInt32();
			byte[] data = m_Socket.ReadBytes(size);
			using (MemoryStream stream = new MemoryStream(data))
			{
				Image image = Image.FromStream(stream);
				image.Save("table." + m_Player.Login + ".png", ImageFormat.Png);
			}
		}
		protected void OnRequestTableImage()
		{
			string playerName = m_Socket.ReadString();
			if (File.Exists("table." + playerName + ".png"))
			{
				Image image = Image.FromFile("table." + playerName + ".png");
				lock (m_Socket)
				{
					m_Socket.WriteInt32((int)PokerProtocolMessageType.TableImage);
					m_Socket.WriteString(playerName);
					m_Socket.WriteImage(image);
				}
			}
		}
		protected void OnBankroll()
		{
			lock (m_Socket)
			{
				m_Socket.WriteInt32((int)PokerProtocolMessageType.Bankroll);
				m_Socket.WriteUInt64(m_Player.BankRoll);
			}
		}
		protected void OnTournamentLobby()
		{
			string tournamentName = m_Socket.ReadString();
			PokerTournamentConfiguration tournament = m_Server.GetTournament(tournamentName);
			if (tournament != null)
			{
				lock (m_Socket)
				{
					m_Socket.WriteInt32((int)PokerProtocolMessageType.TournamentLobby);
					m_Socket.WriteString(tournament.TournamentName);
					m_Socket.WriteBoolean(tournament.IsRegistered(m_Player));
					m_Socket.WriteInt32(tournament.EntryCount);
					m_Socket.WriteUInt64(tournament.EntryPrice);
					m_Socket.WriteUInt64(tournament.EntryRake);
					m_Socket.WriteInt32(tournament.MinimumEntries);
					m_Socket.WriteInt32(tournament.MaximumEntries);
					m_Socket.WriteDateTime(tournament.StartTime);
					m_Socket.WriteUInt64(tournament.StartingChips);
					m_Socket.WriteInt32(tournament.PlayersLeft);
				}
			}
		}
		protected void OnTournamentRegister()
		{
			string tournamentName = m_Socket.ReadString();
			PokerTournamentConfiguration tournament = m_Server.GetTournament(tournamentName);
			if (tournament != null && m_Player != null)
			{
				if (tournament.Enter(m_Player))
				{
					lock (m_Socket)
					{
						m_Socket.WriteInt32((int)PokerProtocolMessageType.TournamentRegister);
						m_Socket.WriteString(tournament.TournamentName);
					}
				}
			}
		}
		protected void OnTournamentUnregister()
		{
			string tournamentName = m_Socket.ReadString();
			PokerTournamentConfiguration tournament = m_Server.GetTournament(tournamentName);
			if (tournament != null && m_Player != null)
			{
				if (tournament.Unregister(m_Player))
				{
					lock (m_Socket)
					{
						m_Socket.WriteInt32((int)PokerProtocolMessageType.TournamentUnregister);
						m_Socket.WriteString(tournament.TournamentName);
					}
				}
			}
		}
		protected void OnTournamentLobbyPlayers()
		{
			string tournamentName = m_Socket.ReadString();
			PokerTournamentConfiguration tournament = m_Server.GetTournament(tournamentName);
			if (tournament != null && m_Player != null)
			{
				lock (m_Socket)
				{
					m_Socket.WriteInt32((int)PokerProtocolMessageType.TournamentLobbyPlayers);
					m_Socket.WriteString(tournament.TournamentName);
					List<KeyValuePair<string, ulong>> players = tournament.PlayersAmount;
					m_Socket.WriteInt32(players.Count);
					foreach (KeyValuePair<string, ulong> pair in players)
					{
						m_Socket.WriteString(pair.Key);
						m_Socket.WriteUInt64(pair.Value);
					}
				}
			}
		}

		protected void OnTournamentLobbyBettingStructure()
		{
			string tournamentName = m_Socket.ReadString();
			PokerTournamentConfiguration tournament = m_Server.GetTournament(tournamentName);
			if (tournament != null && m_Player != null)
			{
				lock (m_Socket)
				{
					m_Socket.WriteInt32((int)PokerProtocolMessageType.TournamentLobbyBettingStructure);
					m_Socket.WriteString(tournament.TournamentName);
					PokerTournamentConfiguration.BettingStructureEntry[] betting = tournament.BettingStructure;
					if (betting == null)
						m_Socket.WriteInt32(0);
					else
					{
						m_Socket.WriteInt32(betting.Length);
						foreach (PokerTournamentConfiguration.BettingStructureEntry entry in betting)
						{
							m_Socket.WriteInt32(entry.Level);
							m_Socket.WriteUInt64(entry.SmallBlind);
							m_Socket.WriteUInt64(entry.BigBlind);
							m_Socket.WriteUInt64(entry.Ante);
							m_Socket.WriteInt32(entry.Duration);
						}
					}
				}
			}
		}

		protected void OnTournamentLobbyPayoutStructure()
		{
			string tournamentName = m_Socket.ReadString();
			PokerTournamentConfiguration tournament = m_Server.GetTournament(tournamentName);
			if (tournament != null && m_Player != null)
			{
				lock (m_Socket)
				{
					m_Socket.WriteInt32((int)PokerProtocolMessageType.TournamentLobbyPayoutStructure);
					m_Socket.WriteString(tournament.TournamentName);
					PokerTournamentConfiguration.PayoutEntry[] payout = tournament.PayoutStructure;
					if (payout == null)
						m_Socket.WriteInt32(0);
					else
					{
						m_Socket.WriteInt32(payout.Length);
						foreach (PokerTournamentConfiguration.PayoutEntry entry in payout)
						{
							m_Socket.WriteInt32(entry.FirstPosition);
							m_Socket.WriteInt32(entry.LastPosition);
							m_Socket.WriteUInt64(entry.Amount);
						}
					}
				}
			}
		}

		protected void OnTournamentRebuy()
		{
			string tournamentName = m_Socket.ReadString();
			int actionKey = m_Socket.ReadInt32();
			byte rebuyCount = m_Socket.ReadByte();
			if (m_Player != null)
			{
				PokerTable table = m_Player.GetTableByTournamentName(tournamentName);
				if (table != null)
				{
					PokerTournamentConfiguration config = table.Configuration as PokerTournamentConfiguration;
					if (config != null && m_Player.BankRoll >= config.RebuyPrice * rebuyCount)
					{
						m_Player.BankRoll -= (config.RebuyPrice * rebuyCount);
						m_Player.SetRebuy(table, rebuyCount, actionKey);
					}
				}
			}
		}

		public override string ToString() { return m_Player == null ? m_Socket.RemoteEndPoint.ToString() : m_Player.Login; }
	}
}
